from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

class ImageHasher:
    def __init__(self):
        self.hashes = []    # 存储计算得到的 dHash 值

    def _generate_dhash_bits(self, pixels):
        """
        生成给定像素数据的 dHash 二进制序列。

        Args:
            pixels (list): 灰度化并调整大小后的图片像素数据列表。

        Returns:
            list: 包含 64 个元素的列表，每个元素为 0 或 1，表示 dHash 的二进制序列。
        """
        dhash_bits = []
        for y in range(8):
            for x in range(8):
                left_pixel = pixels[y * 9 + x]
                right_pixel = pixels[y * 9 + x + 1]
                if left_pixel > right_pixel:
                    dhash_bits.append(1)
                else:
                    dhash_bits.append(0)
        return dhash_bits
    def calculate_dhash(self, image_path):
        """
        计算给定图片的 dHash 值。

        Args:
            image_path (str): 图片文件的路径。

        Returns:
            str: 图片的 64 位二进制 dHash 字符串。
        """
        img = Image.open(image_path).convert("L").resize((9, 8), Image.Resampling.LANCZOS)
        pixels = list(img.getdata())
        dhash_bits = self._generate_dhash_bits(pixels)
        return ''.join(map(str, dhash_bits))

    def hamming_distance(self, hash1, hash2):
        """
        计算两个哈希字符串之间的汉明距离。
        汉明距离越小，表示两个哈希越相似。
        """
        if len(hash1) != len(hash2):
            raise ValueError("哈希字符串长度不一致")
        distance = 0
        for i in range(len(hash1)):
            if hash1[i] != hash2[i]:
                distance += 1
        return distance

    def generate_variations(self, original_image_path):
        """
        生成原始图片的变体，包括缩小版和亮度增强版。

        Args:
            original_image_path (str): 原始图片的文件路径。

        Returns:
            list: 包含原始图片路径、缩小版图片路径和亮度增强版图片路径的列表。
        """
        original_img = Image.open(original_image_path)
        resized_img = original_img.resize((original_img.width // 2, original_img.height // 2))
        resized_img_path = './Image_deduplication/resized_image.jpg'
        resized_img.save(resized_img_path)

        bright_img = original_img.point(lambda p: p * 1.5)
        bright_img_path = './Image_deduplication/brightened_image.jpg'
        bright_img.save(bright_img_path)

        return [original_image_path, resized_img_path, bright_img_path]

    def visualize_process(self, original_image_path):
        """
        可视化 dHash 处理过程，包括原始图片、处理后的灰度图片和二进制 dHash 图片。

        Args:
            original_image_path (str): 原始图片的文件路径。
        """
        fig, axes = plt.subplots(1, 3, figsize=(15, 5))
        original_img = Image.open(original_image_path)
        axes[0].imshow(original_img)
        axes[0].set_title('Original Image')
        axes[0].axis('off')

        processed_img = original_img.convert("L").resize((9, 8), Image.Resampling.LANCZOS)
        axes[1].imshow(processed_img, cmap='gray')
        axes[1].set_title('Processed Image (before dHash)')
        axes[1].axis('off')

        pixels = list(processed_img.getdata())
        dhash_bits = self._generate_dhash_bits(pixels)
        dhash_img = np.array(dhash_bits).reshape((8, 8))
        axes[2].imshow(dhash_img, cmap='binary')
        axes[2].set_title('Binary Image (dHash)')
        axes[2].axis('off')

        plt.show()

if __name__ == "__main__":
    original_image_path = './Image_deduplication/original_image.jpg'
    different_image_path = './Image_deduplication/different.jpg'
    hasher = ImageHasher()

    try:
        image_paths = hasher.generate_variations(original_image_path) + [different_image_path]

        print("--- 计算 dHash 值 ---")
        for path in image_paths:
            img_hash = hasher.calculate_dhash(path)
            hasher.hashes.append(img_hash)
            print(f"{path} dHash: {img_hash}")

        print("\n--- 计算汉明距离 ---")
        original_hash = hasher.hashes[0]
        for i in range(1, len(hasher.hashes)):
            print(f"Original vs {image_paths[i]} Distance: {hasher.hamming_distance(original_hash, hasher.hashes[i])}")

        hasher.visualize_process(original_image_path)
    except FileNotFoundError as e:
        print(f"错误：图片文件未找到。请检查 'original_image.png' 和 'different.png' 是否存在于当前目录下。")
        print(f"具体错误：{e}")
    except Exception as e:
        print(f"发生了一个错误：{e}")